import os
import re
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
from pathlib import Path
import json
from collections import defaultdict

class MarkdownAttributeEditor:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("Markdown属性批量编辑器")
        self.root.geometry("900x700")
        
        self.directory = None
        self.reference_directory = None
        self.md_files = []
        self.attribute_references = defaultdict(set)
        
        # 只保留共有属性
        self.attributes = ["tags", "type", "罩杯", "风格", "气质", "体型", "演技"]
        self.array_attributes = ["tags", "风格", "气质", "体型", "演技"]
        
        self.create_widgets()
    
    def create_widgets(self):
        # 主框架
        main_frame = tk.Frame(self.root)
        main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 目录选择框架
        dir_frame = tk.LabelFrame(main_frame, text="目录选择", padx=10, pady=10)
        dir_frame.pack(fill=tk.X, pady=5)
        
        # 处理目录选择按钮
        dir_button_frame = tk.Frame(dir_frame)
        dir_button_frame.pack(fill=tk.X, pady=5)
        
        tk.Button(dir_button_frame, text="选择处理目录", command=self.select_directory).pack(side=tk.LEFT, padx=5)
        tk.Button(dir_button_frame, text="扫描文件", command=self.scan_files).pack(side=tk.LEFT, padx=5)
        
        self.dir_label = tk.Label(dir_frame, text="未选择目录", fg="blue")
        self.dir_label.pack(anchor="w")
        
        # 参考目录框架
        ref_frame = tk.LabelFrame(main_frame, text="参考值", padx=10, pady=10)
        ref_frame.pack(fill=tk.X, pady=5)
        
        # 参考目录选择按钮
        ref_button_frame = tk.Frame(ref_frame)
        ref_button_frame.pack(fill=tk.X, pady=5)
        
        tk.Button(ref_button_frame, text="选择参考目录", command=self.select_reference_directory).pack(side=tk.LEFT, padx=5)
        tk.Button(ref_button_frame, text="加载参考值", command=self.load_reference_values).pack(side=tk.LEFT, padx=5)
        
        self.ref_dir_label = tk.Label(ref_frame, text="未选择参考目录", fg="green")
        self.ref_dir_label.pack(anchor="w")
        
        self.ref_status_label = tk.Label(ref_frame, text="参考值: 未加载", fg="red")
        self.ref_status_label.pack(anchor="w")
        
        # 批量操作框架
        action_frame = tk.LabelFrame(main_frame, text="批量操作", padx=10, pady=10)
        action_frame.pack(fill=tk.BOTH, expand=True, pady=5)
        
        # 操作类型选择
        operation_frame = tk.Frame(action_frame)
        operation_frame.pack(fill=tk.X, pady=5)
        
        self.operation_var = tk.StringVar(value="add")
        
        tk.Radiobutton(operation_frame, text="批量添加", variable=self.operation_var, value="add").pack(side=tk.LEFT, padx=10)
        tk.Radiobutton(operation_frame, text="批量删除", variable=self.operation_var, value="delete").pack(side=tk.LEFT, padx=10)
        tk.Radiobutton(operation_frame, text="批量修改", variable=self.operation_var, value="modify").pack(side=tk.LEFT, padx=10)
        
        # 属性选择
        attr_frame = tk.Frame(action_frame)
        attr_frame.pack(fill=tk.X, pady=5)
        
        tk.Label(attr_frame, text="选择属性:").pack(side=tk.LEFT)
        self.attr_var = tk.StringVar(value=self.attributes[0])
        attr_combo = ttk.Combobox(attr_frame, textvariable=self.attr_var, values=self.attributes, width=15)
        attr_combo.pack(side=tk.LEFT, padx=5)
        
        # 值输入框架
        value_frame = tk.Frame(action_frame)
        value_frame.pack(fill=tk.X, pady=5)
        
        tk.Label(value_frame, text="值 A:").pack(side=tk.LEFT)
        self.value_a_entry = tk.Entry(value_frame, width=30)
        self.value_a_entry.pack(side=tk.LEFT, padx=5)
        
        tk.Label(value_frame, text="值 B:").pack(side=tk.LEFT)
        self.value_b_entry = tk.Entry(value_frame, width=30)
        self.value_b_entry.pack(side=tk.LEFT, padx=5)
        
        # 参考值按钮
        self.ref_button = tk.Button(value_frame, text="参考值", command=self.show_reference, state="disabled")
        self.ref_button.pack(side=tk.LEFT, padx=5)
        
        # 选项框架
        option_frame = tk.Frame(action_frame)
        option_frame.pack(fill=tk.X, pady=5)
        
        self.case_sensitive_var = tk.BooleanVar(value=False)
        tk.Checkbutton(option_frame, text="区分大小写", variable=self.case_sensitive_var).pack(side=tk.LEFT, padx=10)
        
        self.trim_spaces_var = tk.BooleanVar(value=True)
        tk.Checkbutton(option_frame, text="去除首尾空格", variable=self.trim_spaces_var).pack(side=tk.LEFT, padx=10)
        
        # 文件统计框架
        stats_frame = tk.Frame(action_frame)
        stats_frame.pack(fill=tk.X, pady=5)
        
        self.stats_label = tk.Label(stats_frame, text="请先选择处理目录并扫描文件")
        self.stats_label.pack(anchor="w")
        
        # 预览框架
        preview_frame = tk.Frame(action_frame)
        preview_frame.pack(fill=tk.BOTH, expand=True, pady=5)
        
        tk.Label(preview_frame, text="操作预览:").pack(anchor="w")
        
        self.preview_text = tk.Text(preview_frame, height=12)
        self.preview_text.pack(fill=tk.BOTH, expand=True)
        
        # 按钮框架
        button_frame = tk.Frame(action_frame)
        button_frame.pack(fill=tk.X, pady=10)
        
        tk.Button(button_frame, text="预览操作", command=self.preview_operation, bg="#f0f0f0").pack(side=tk.LEFT, padx=5)
        tk.Button(button_frame, text="执行批量操作", command=self.execute_operation, bg="#4CAF50", fg="white").pack(side=tk.LEFT, padx=5)
        tk.Button(button_frame, text="清空输入", command=self.clear_inputs).pack(side=tk.RIGHT, padx=5)
    
    def select_directory(self):
        """选择处理目录"""
        directory = filedialog.askdirectory(title="选择要批量处理的Markdown文件目录")
        if directory:
            self.directory = directory
            self.dir_label.config(text=f"处理目录: {os.path.basename(directory)}")
            # 自动扫描文件
            self.scan_files()
    
    def select_reference_directory(self):
        """选择参考目录"""
        directory = filedialog.askdirectory(title="选择参考目录")
        if directory:
            self.reference_directory = directory
            self.ref_dir_label.config(text=f"参考目录: {os.path.basename(directory)}")
    
    def scan_files(self):
        """扫描目录中的文件"""
        if not self.directory:
            messagebox.showwarning("警告", "请先选择处理目录")
            return
        
        self.md_files = []
        try:
            for file_path in Path(self.directory).rglob("*.md"):
                self.md_files.append(file_path)
            
            file_count = len(self.md_files)
            self.stats_label.config(text=f"找到 {file_count} 个Markdown文件")
            
            if file_count > 0:
                self.preview_text.delete(1.0, tk.END)
                self.preview_text.insert(1.0, f"✅ 已扫描到 {file_count} 个Markdown文件\n\n请设置以下参数后点击'预览操作':\n1. 选择操作类型（添加/删除/修改）\n2. 选择要操作的属性\n3. 输入相应的值")
            else:
                self.preview_text.delete(1.0, tk.END)
                self.preview_text.insert(1.0, "❌ 目录中没有找到Markdown文件")
                messagebox.showwarning("警告", "目录中没有找到Markdown文件")
                
        except Exception as e:
            messagebox.showerror("错误", f"扫描文件时出错: {e}")
    
    def load_reference_values(self):
        """加载参考值"""
        if not self.reference_directory:
            messagebox.showwarning("警告", "请先选择参考目录")
            return
        
        try:
            self.collect_attribute_references()
            
            ref_count = sum(len(values) for values in self.attribute_references.values())
            
            self.ref_status_label.config(
                text=f"参考值: 已加载 {ref_count} 个值",
                fg="green"
            )
            
            # 启用参考按钮
            self.ref_button.config(state="normal")
            
            messagebox.showinfo("参考值已加载", f"已从参考目录中收集到 {ref_count} 个属性参考值")
            
        except Exception as e:
            messagebox.showerror("错误", f"加载参考值时出错: {e}")
    
    def clear_inputs(self):
        """清空输入框"""
        self.value_a_entry.delete(0, tk.END)
        self.value_b_entry.delete(0, tk.END)
        self.preview_text.delete(1.0, tk.END)
        self.preview_text.insert(1.0, "输入框已清空，请重新设置操作参数")
    
    def show_reference(self):
        """显示参考值"""
        attr = self.attr_var.get()
        if not self.attribute_references[attr]:
            messagebox.showinfo("参考", f"属性 '{attr}' 没有可用的参考值")
            return
        
        ref_window = tk.Toplevel(self.root)
        ref_window.title(f"{attr} 参考值 (共{len(self.attribute_references[attr])}个)")
        ref_window.geometry("500x400")
        
        # 搜索框
        search_frame = tk.Frame(ref_window)
        search_frame.pack(fill=tk.X, padx=10, pady=5)
        
        tk.Label(search_frame, text="搜索:").pack(side=tk.LEFT)
        search_var = tk.StringVar()
        search_entry = tk.Entry(search_frame, textvariable=search_var, width=30)
        search_entry.pack(side=tk.LEFT, padx=5)
        search_entry.focus()
        
        # 列表框
        list_frame = tk.Frame(ref_window)
        list_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
        
        listbox = tk.Listbox(list_frame, font=("Arial", 10))
        listbox.pack(fill=tk.BOTH, expand=True)
        
        scrollbar = ttk.Scrollbar(listbox, orient=tk.VERTICAL, command=listbox.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        listbox.config(yscrollcommand=scrollbar.set)
        
        # 填充参考值
        for value in sorted(self.attribute_references[attr]):
            listbox.insert(tk.END, value)
        
        # 按钮框架
        button_frame = tk.Frame(ref_window)
        button_frame.pack(fill=tk.X, padx=10, pady=5)
        
        def insert_value_a():
            selection = listbox.curselection()
            if selection:
                value = listbox.get(selection[0])
                self.value_a_entry.delete(0, tk.END)
                self.value_a_entry.insert(0, value)
        
        def insert_value_b():
            selection = listbox.curselection()
            if selection:
                value = listbox.get(selection[0])
                self.value_b_entry.delete(0, tk.END)
                self.value_b_entry.insert(0, value)
        
        tk.Button(button_frame, text="插入到值A", command=insert_value_a).pack(side=tk.LEFT, padx=5)
        tk.Button(button_frame, text="插入到值B", command=insert_value_b).pack(side=tk.LEFT, padx=5)
        tk.Button(button_frame, text="关闭", command=ref_window.destroy).pack(side=tk.RIGHT, padx=5)
        
        # 搜索功能
        def update_list(*args):
            search_term = search_var.get().lower()
            listbox.delete(0, tk.END)
            for value in sorted(self.attribute_references[attr]):
                if search_term in value.lower():
                    listbox.insert(tk.END, value)
        
        search_var.trace("w", update_list)
        
        # 双击插入到值A
        listbox.bind("<Double-Button-1>", lambda e: insert_value_a())
    
    def collect_attribute_references(self):
        """收集属性参考值"""
        if not self.reference_directory:
            return
        
        for attr in self.attributes:
            self.attribute_references[attr].clear()
        
        reference_files = []
        for file_path in Path(self.reference_directory).rglob("*.md"):
            reference_files.append(file_path)
        
        for file_path in reference_files:
            try:
                with open(file_path, 'r', encoding='utf-8') as f:
                    content = f.read()
                
                front_matter = self.parse_front_matter(content)
                
                for attr in self.attributes:
                    if attr in front_matter:
                        value = front_matter[attr]
                        if isinstance(value, list):
                            for item in value:
                                if item and str(item).strip():
                                    self.attribute_references[attr].add(str(item).strip())
                        else:
                            if value and str(value).strip():
                                self.attribute_references[attr].add(str(value).strip())
            except Exception as e:
                print(f"读取参考文件失败 {file_path}: {e}")
    
    def parse_front_matter(self, content):
        """解析front matter"""
        front_matter = {}
        match = re.search(r'^---\s*\n(.*?)\n---', content, re.DOTALL)
        if match:
            front_matter_text = match.group(1)
            lines = front_matter_text.split('\n')
            current_key = None
            current_list = []
            
            for line in lines:
                line = line.strip()
                if not line:
                    continue
                
                if ':' in line and not line.startswith(' '):
                    if current_key and current_list:
                        front_matter[current_key] = current_list
                        current_list = []
                    
                    key, value = line.split(':', 1)
                    key = key.strip()
                    value = value.strip()
                    
                    if value.startswith('[') and value.endswith(']'):
                        try:
                            front_matter[key] = json.loads(value)
                        except:
                            items = value[1:-1].split(',')
                            front_matter[key] = [item.strip() for item in items if item.strip()]
                    elif value.startswith('-'):
                        current_key = key
                        current_list = [value[1:].strip()]
                    elif value:
                        front_matter[key] = value
                    else:
                        current_key = key
                        current_list = []
                elif line.startswith('- ') and current_key:
                    current_list.append(line[2:].strip())
            
            if current_key and current_list:
                front_matter[current_key] = current_list
        
        return front_matter
    
    def preview_operation(self):
        """预览操作"""
        if not self.md_files:
            messagebox.showwarning("警告", "请先选择处理目录并扫描文件")
            return
        
        attr = self.attr_var.get()
        value_a = self.value_a_entry.get().strip()
        operation = self.operation_var.get()
        
        if operation != "delete" and not value_a:
            messagebox.showwarning("警告", "请输入值A")
            return
        
        if operation == "modify":
            value_b = self.value_b_entry.get().strip()
            if not value_b:
                messagebox.showwarning("警告", "修改操作需要输入值B")
                return
        
        # 分析操作影响
        affected_files = []
        
        for file_path in self.md_files:
            try:
                with open(file_path, 'r', encoding='utf-8') as f:
                    content = f.read()
                
                front_matter = self.parse_front_matter(content)
                will_be_affected = False
                
                if operation == "add":
                    will_be_affected = self.will_be_affected_by_add(front_matter, attr, value_a)
                elif operation == "delete":
                    will_be_affected = self.will_be_affected_by_delete(front_matter, attr, value_a)
                elif operation == "modify":
                    will_be_affected = self.will_be_affected_by_modify(front_matter, attr, value_a, value_b)
                
                if will_be_affected:
                    affected_files.append(file_path.name)
                    
            except Exception as e:
                print(f"分析文件失败 {file_path}: {e}")
        
        # 显示预览
        self.preview_text.delete(1.0, tk.END)
        
        operation_name = self.get_operation_name(operation)
        value_b_display = self.value_b_entry.get().strip() if operation == "modify" else "N/A"
        
        preview_content = (
            f"📊 操作预览\n"
            f"{'='*50}\n"
            f"操作类型: {operation_name}\n"
            f"属性: {attr}\n"
            f"值A: {value_a if value_a else '所有值'}\n"
            f"值B: {value_b_display}\n"
            f"影响文件数: {len(affected_files)}/{len(self.md_files)}\n"
            f"{'='*50}\n\n"
        )
        
        if affected_files:
            preview_content += f"✅ 以下 {len(affected_files)} 个文件将受到影响:\n\n"
            preview_content += "\n".join(affected_files[:15])
            if len(affected_files) > 15:
                preview_content += f"\n... 还有 {len(affected_files)-15} 个文件"
            
            preview_content += f"\n\n💡 点击'执行批量操作'按钮开始处理"
        else:
            preview_content += "❌ 没有文件会受到影响，请检查操作参数"
        
        self.preview_text.insert(1.0, preview_content)
    
    def will_be_affected_by_add(self, front_matter, attr, value):
        if attr not in front_matter:
            return True
        
        current_values = front_matter[attr]
        if not isinstance(current_values, list):
            current_values = [current_values]
        
        return not self.value_exists(current_values, value)
    
    def will_be_affected_by_delete(self, front_matter, attr, value):
        if attr not in front_matter:
            return False
        
        if not value:
            return True
        
        current_values = front_matter[attr]
        if not isinstance(current_values, list):
            current_values = [current_values]
        
        return self.value_exists(current_values, value)
    
    def will_be_affected_by_modify(self, front_matter, attr, value_a, value_b):
        if attr not in front_matter:
            return False
        
        current_values = front_matter[attr]
        if not isinstance(current_values, list):
            current_values = [current_values]
        
        has_value_a = self.value_exists(current_values, value_a)
        has_value_b = self.value_exists(current_values, value_b) if value_b != value_a else True
        
        return has_value_a and not has_value_b
    
    def value_exists(self, values, target_value):
        if not target_value:
            return False
        
        case_sensitive = self.case_sensitive_var.get()
        trim_spaces = self.trim_spaces_var.get()
        
        for value in values:
            processed_value = str(value)
            processed_target = str(target_value)
            
            if trim_spaces:
                processed_value = processed_value.strip()
                processed_target = processed_target.strip()
            
            if not case_sensitive:
                processed_value = processed_value.lower()
                processed_target = processed_target.lower()
            
            if processed_value == processed_target:
                return True
        
        return False
    
    def execute_operation(self):
        """执行批量操作"""
        if not self.md_files:
            messagebox.showwarning("警告", "请先选择处理目录并扫描文件")
            return
        
        attr = self.attr_var.get()
        value_a = self.value_a_entry.get().strip()
        value_b = self.value_b_entry.get().strip()
        operation = self.operation_var.get()
        
        if operation != "delete" and not value_a:
            messagebox.showwarning("警告", "请输入值A")
            return
        
        if operation == "modify" and not value_b:
            messagebox.showwarning("警告", "修改操作需要输入值B")
            return
        
        # 确认对话框
        operation_name = self.get_operation_name(operation)
        confirm_message = (
            f"确定要执行以下批量操作吗？\n\n"
            f"操作类型: {operation_name}\n"
            f"属性: {attr}\n"
            f"值A: {value_a if value_a else '所有值'}\n"
            f"值B: {value_b if operation == 'modify' else 'N/A'}\n"
            f"文件数量: {len(self.md_files)} 个\n\n"
            f"此操作不可撤销，请确保已备份重要文件！"
        )
        
        result = messagebox.askyesno("确认操作", confirm_message)
        if not result:
            return
        
        # 执行操作
        processed_count = 0
        error_count = 0
        error_messages = []
        
        for i, file_path in enumerate(self.md_files):
            try:
                with open(file_path, 'r', encoding='utf-8') as f:
                    content = f.read()
                
                front_matter = self.parse_front_matter(content)
                modified = False
                
                if operation == "add":
                    modified = self.apply_add_operation(front_matter, attr, value_a)
                elif operation == "delete":
                    modified = self.apply_delete_operation(front_matter, attr, value_a)
                elif operation == "modify":
                    modified = self.apply_modify_operation(front_matter, attr, value_a, value_b)
                
                if modified:
                    new_content = self.generate_content_with_front_matter(content, front_matter)
                    with open(file_path, 'w', encoding='utf-8') as f:
                        f.write(new_content)
                    processed_count += 1
                    
                # 更新进度（每10个文件更新一次）
                if i % 10 == 0:
                    self.root.update()
                    
            except Exception as e:
                error_count += 1
                error_messages.append(f"{file_path.name}: {str(e)}")
                print(f"处理文件失败 {file_path}: {e}")
        
        # 显示结果
        result_message = (
            f"✅ 批量操作完成！\n\n"
            f"成功处理: {processed_count} 个文件\n"
            f"处理失败: {error_count} 个文件\n"
            f"总文件数: {len(self.md_files)}"
        )
        
        if error_messages:
            result_message += f"\n\n❌ 错误文件 ({min(5, len(error_messages))}个):\n" + "\n".join(error_messages[:5])
            if len(error_messages) > 5:
                result_message += f"\n... 还有 {len(error_messages)-5} 个错误"
        
        messagebox.showinfo("操作完成", result_message)
        
        # 重新扫描以更新状态
        self.scan_files()
    
    def apply_add_operation(self, front_matter, attr, value):
        if attr not in front_matter:
            front_matter[attr] = [value] if attr in self.array_attributes else value
            return True
        
        current_values = front_matter[attr]
        if attr in self.array_attributes:
            if not isinstance(current_values, list):
                current_values = [current_values]
            
            if not self.value_exists(current_values, value):
                current_values.append(value)
                front_matter[attr] = current_values
                return True
        else:
            if not current_values:
                front_matter[attr] = value
                return True
        
        return False
    
    def apply_delete_operation(self, front_matter, attr, value):
        if attr not in front_matter:
            return False
        
        if not value:
            del front_matter[attr]
            return True
        
        current_values = front_matter[attr]
        if attr in self.array_attributes:
            if not isinstance(current_values, list):
                current_values = [current_values]
            
            new_values = []
            for val in current_values:
                if not self.value_exists([val], value):
                    new_values.append(val)
            
            if len(new_values) != len(current_values):
                front_matter[attr] = new_values if new_values else None
                return True
        else:
            if self.value_exists([current_values], value):
                front_matter[attr] = None
                return True
        
        return False
    
    def apply_modify_operation(self, front_matter, attr, value_a, value_b):
        if attr not in front_matter:
            return False
        
        current_values = front_matter[attr]
        modified = False
        
        if attr in self.array_attributes:
            if not isinstance(current_values, list):
                current_values = [current_values]
            
            new_values = []
            for val in current_values:
                if self.value_exists([val], value_a):
                    new_values.append(value_b)
                    modified = True
                else:
                    new_values.append(val)
            
            if modified:
                front_matter[attr] = new_values
        else:
            if self.value_exists([current_values], value_a):
                front_matter[attr] = value_b
                modified = True
        
        return modified
    
    def generate_content_with_front_matter(self, content, front_matter):
        front_matter_match = re.search(r'^---\s*\n(.*?)\n---', content, re.DOTALL)
        
        if front_matter_match:
            old_front_matter = front_matter_match.group(0)
            new_front_matter = self.front_matter_to_string(front_matter)
            return content.replace(old_front_matter, new_front_matter)
        else:
            new_front_matter = self.front_matter_to_string(front_matter)
            return new_front_matter + '\n' + content
    
    def front_matter_to_string(self, front_matter):
        lines = ["---"]
        for key, value in front_matter.items():
            if value is None:
                continue
            if isinstance(value, list):
                if value:
                    lines.append(f"{key}:")
                    for item in value:
                        lines.append(f"  - {item}")
            else:
                lines.append(f"{key}: {value}")
        lines.append("---")
        return "\n".join(lines)
    
    def get_operation_name(self, operation):
        names = {
            "add": "批量添加",
            "delete": "批量删除", 
            "modify": "批量修改"
        }
        return names.get(operation, "未知操作")
    
    def run(self):
        self.root.mainloop()

if __name__ == "__main__":
    editor = MarkdownAttributeEditor()
    editor.run()